perm filename TCPPRI.C[IP,SYS] blob
sn#680202 filedate 1982-10-07 generic text, type T, neo UTF8
#include "../h/param.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/imp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"
/*
* TCP finite state machine primitives
*
* These routines are called from the procedures in tcp_procs.c to do low
* level protocol functions.
*/
/*
* Send a tcp segment
*/
send_pkt(tp, flags, len, dat)
register struct tcb *tp;
register int flags;
int len;
struct mbuf *dat;
{
register struct mbuf *m;
register struct th *t;
register struct ucb *up;
register i;
short *p;
struct work w;
if ((m = m_get(1)) == NULL)
return(FALSE);
up = tp->t_ucb;
/*
* Build tcp leader at bottom of new buffer to leave room for lower
* level leaders. Leave an extra four bytes for TCP max segment size
* option, which is sent in SYN packets.
*/
m->m_off = MSIZE - sizeof(struct th) - 4;
m->m_len = sizeof(struct th);
m->m_next = dat;
t = mtod(m, struct th *);
/*
* Adjust data length for SYN and FIN. Also, insert max seg size
* option for SYN.
*/
if (flags&T_FIN)
len--;
if (flags&T_SYN)
#ifdef NOTCPOPTS
len--;
#else
{
m->m_len += TCP_MAXSEG_OPTLEN;
len += TCP_MAXSEG_OPTLEN - 1; /* SYN occupies seq space */
t->t_off = (TCPSIZE + TCP_MAXSEG_OPTLEN) >> 2;
p = (short *)((int)t + sizeof(struct th));
*p++ = TCP_MAXSEG_OPTHDR;
*p = short_to_net(up->uc_srcif->if_mtu - TCPIPMAX);
} else
#endif
t->t_off = TCPSIZE >> 2;
t->t_len = short_to_net(len + TCPSIZE);
/*
* Set up Internet addresses in IP part of leader, and fill in
* the TCP part.
*/
t->t_s = up->uc_local;
t->t_d = up->uc_host;
t->t_src = short_to_net(tp->t_lport);
t->t_dst = short_to_net(tp->t_fport);
t->t_seq = long_to_net(tp->snd_nxt);
t->t_ackno = long_to_net(tp->rcv_nxt);
if (tp->snd_rst) {
flags |= T_RST;
flags &= ~T_SYN;
}
if (tp->snd_urg)
flags |= T_URG;
if (tp->syn_rcvd)
flags |= T_ACK;
t->t_flags = flags;
/*
* If we sent a zero window, we should try to send a non-zero ACK ASAP.
*/
if ((i = rcv_resource(tp)) == 0)
tp->sent_zero = TRUE;
else
tp->sent_zero = FALSE;
t->t_win = short_to_net(i);
t->t_urp = short_to_net(tp->snd_urp-tp->snd_nxt);
/*
* Get rest of IP part of leader ready for checksum and IP processing.
*/
t->t_next = NULL;
t->t_prev = NULL;
t->t_x1 = 0;
t->t_x2 = 0;
t->t_sum = 0;
t->t_pr = TCPROTO;
#ifndef mbb
t->t_sum = cksum(m, len + sizeof(struct th));
#else
t->t_x0 = 0;
t->t_x00 = 0;
i = cksum(m, len + sizeof(struct th));
t->t_sum = short_to_net(i);
#endif mbb
i = ip_send(up, m, TCPROTO, len+TCPSIZE, tp->t_optlen, tp->t_opts,
FALSE);
if (up->uc_flags & UDEBUG) {
w.w_dat = (char *)t;
w.w_stype = i;
tcp_debug(tp, &w, INRECV, -1);
}
return(i);
}
/*
* Find the first empty spot in rcv buffer
*/
sequence firstempty(tp)
register struct tcb *tp;
{
register struct th *p, *q;
if ((p = tp->t_rcv_next) == (struct th *)tp ||
SEQ_LT(tp->rcv_nxt, p->t_seq))
return(tp->rcv_nxt);
while ((q = p->t_next) != (struct th *)tp &&
SEQ_EQ(t_end(p)+1, q->t_seq))
p = q;
return(t_end(p) + 1);
}
/*
* Get number of rcv bufs available
*/
rcv_resource(tp)
register struct tcb *tp;
{
register struct ucb *up = tp->t_ucb;
register struct th *t;
register i;
/* first count bufs in user receive queue */
i = (up->uc_rcv - up->uc_rsize) * MLEN;
/* now reduce by segments in sequencing queue */
for (t = tp->t_rcv_next; i > 0 && t != (struct th *)tp; t = t->t_next)
i -= t->t_len;
return(i < 0 ? 0 : i);
}
/*
* Copy mbuf chain from snd buffer for sends
*/
struct mbuf *snd_copy(tp, start, end)
struct tcb *tp;
sequence start, end;
{
register struct mbuf *m, *n;
register sequence off;
register adj, len;
struct mbuf *top;
/* make sure we have something to copy */
if (SEQ_GEQ(start, end))
return(NULL);
off = tp->snd_una;
if (!tp->syn_acked) /* skip over SYN */
off++;
m = tp->t_ucb->uc_sbuf;
/* find mbuf to start copying */
while (m != NULL && SEQ_GEQ(start, off+m->m_len)) {
off += m->m_len;
m = m->m_next;
}
/* get buffer to copy into */
if (m == NULL || (n = top = m_get(1)) == NULL)
return(NULL);
adj = start - off;
off = start;
goto partial; /* start with partial copy */
do { /* main copy loop */
/* amount we can copy into mbuf */
if ((adj = MLEN - n->m_len) >= m->m_len)
len = m->m_len; /* can copy all */
else
len = adj; /* partial copy */
/* copy as much as possible into mbuf */
bcopy((caddr_t)((int)m + m->m_off),
(caddr_t)((int)n + MHEAD + n->m_len), len);
n->m_len += len;
off += len;
/* must get another mbuf to copy into */
if (SEQ_GT(end, off) && adj < m->m_len) {
if ((n = n->m_next = m_get(1)) == NULL) {
m_freem(top);
return(NULL);
}
/* partial copy into new mbuf */
partial: n->m_off = MHEAD;
n->m_next = NULL;
n->m_len = m->m_len - adj;
bcopy((caddr_t)((int)m + m->m_off + adj),
(caddr_t)((int)n + MHEAD), n->m_len);
off += n->m_len;
}
} while (SEQ_GT(end, off) && (m = m->m_next) != NULL);
/* make sure there was enough in buffer to copy */
if (m == NULL) {
printf("snd_copy: bad copy\n");
m_freem(top);
return(FALSE);
}
/* adjust length of last mbuf copied */
n->m_len -= (off - end);
return(top);
}
/*
* Cancel a timer
*/
t_cancel(tp, timer)
register struct tcb *tp;
register timer;
{
register struct work *w;
/* reset timer value in tcb */
tp->t_timers[timer] = 0;
/* remove any timer work entries already enqueued */
for (w = netcb.n_work; w != NULL; w = w->w_next) {
if (w->w_tcb == tp && w->w_type == ISTIMER &&
w->w_stype == timer)
w->w_type = INOP;
/* THIS SHOULD GO AWAY */
if (w == w->w_next)
w->w_next = NULL;
}
}
/*
* TCP timer update routine
*/
tcp_timeo()
{
register struct tcb *tp;
register i;
/* search through tcb and update active timers */
for (tp = netcb.n_tcb_head; tp != NULL; tp = tp->t_tcb_next) {
for (i = TINIT; i <= TFINACK; i++)
if (tp->t_timers[i] != 0 && --tp->t_timers[i] == 0)
w_alloc(ISTIMER, i, tp, 0);
tp->t_timers[TXMT]++;
}
netcb.n_iss += ISSINCR; /* increment iss */
}
/*
* Tell user proc about change of tcp state
*/
to_user(up, state)
register struct ucb *up;
register short state;
{
/* set user state flag and awaken user proc if asleep */
up->uc_state |= state;
wakeup(up);
/* send urgent signal to user process */
if (state == UURGENT)
psignal(up->uc_proc, SIGURG);
}
/*
* Do TCP option processing
*/
tcp_opt(tp, t, hlen)
register struct tcb *tp;
register struct th *t;
int hlen;
{
register char *p;
register i, len;
p = (char *)((int)t + sizeof(struct th)); /* -> at options */
if ((i = hlen - TCPSIZE) > 0) { /* any options */
while (i > 0)
switch (*p++) {
case TCP_END_OPT:
return;
case TCP_NOP_OPT:
i--;
break;
case TCP_MAXSEG_OPT: /* max segment size */
if (t->t_flags&T_SYN && !tp->syn_rcvd) {
len = short_from_net(
*(short *)((int)p + 1));
tp->t_maxseg =
MIN(tp->t_ucb->uc_srcif->if_mtu -
TCPIPMAX, len);
}
default:
i -= *p;
p += *p - 1;
}
}
}